#include "header.h"

static uint8_t rmt_gear;//远程档位
static udp_data_req_t udp_data_req;//机车运行数据
static locate_t locate;//定位数据

/***************设置CAN接收过滤规则***************/

//recv_id & can_mask == can_id & can_mask
static void can_filter_set(void)
{
	struct can_filter filter[5];

	filter[0].can_id = 0x18800701;//机车向CCU询问控制命令
	filter[0].can_mask = CAN_EFF_MASK;//扩展ID过滤

	filter[1].can_id = 0x0c40ff00;//变频器数据帧
	filter[1].can_mask = CAN_INV_MASK;

	filter[2].can_id = 0x1830ff06;//模组数据帧
	filter[2].can_mask = CAN_MV_MASK;

	filter[3].can_id = 0x18ff1519;//DCDC数据帧
	filter[3].can_mask = CAN_EFF_MASK;

	filter[4].can_id = 0x1860ff01;//整车其它数据帧
	filter[4].can_mask = CAN_LOCO_MASK;
	setsockopt(sock_mtu_can, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter));
}

/***************变频器数据解析***************/

static void get_inv_data(struct can_frame *frame, udp_data_req_t *data)
{
	switch (frame->can_id & CAN_EFF_MASK)
	{
		case 0x0c44ff02://前变频器
			data->inv[0].work_stat = (frame->data[0] >> 6) & 0x3;
			data->inv[0].direction = (frame->data[0] >> 3) & 0x1;
			data->inv[0].fault_level = (frame->data[1] >> 4) & 0xf;
			data->inv[0].speed = (frame->data[2] << 8) | frame->data[3];
			data->inv[0].torque = ((frame->data[4] << 8) | frame->data[5]) / 4;
			break;

		case 0x0c44ff03://后变频器
			data->inv[1].work_stat = (frame->data[0] >> 6) & 0x3;
			data->inv[1].direction = (frame->data[0] >> 3) & 0x1;
			data->inv[1].fault_level = (frame->data[1] >> 4) & 0xf;
			data->inv[1].speed = (frame->data[2] << 8) | frame->data[3];
			data->inv[1].torque = ((frame->data[4] << 8) | frame->data[5]) / 4;
			break;

		case 0x0c46ff02:
			data->inv[0].bus_volt = (frame->data[0] << 8) | frame->data[1];
			data->inv[0].bus_curr = (frame->data[2] << 8) | frame->data[3];
			break;

		case 0x0c46ff03:
			data->inv[1].bus_volt = (frame->data[0] << 8) | frame->data[1];
			data->inv[1].bus_curr = (frame->data[2] << 8) | frame->data[3];
			break;

		case 0x0c48ff02:
			data->inv[0].out_volt = (frame->data[0] << 8) | frame->data[1];
			data->inv[0].out_curr = (frame->data[2] << 8) | frame->data[3];
			data->inv[0].out_power = (frame->data[4] << 8) | frame->data[5];
			data->inv[0].out_freq = frame->data[6];
			break;

		case 0x0c48ff03:
			data->inv[1].out_volt = (frame->data[0] << 8) | frame->data[1];
			data->inv[1].out_curr = (frame->data[2] << 8) | frame->data[3];
			data->inv[1].out_power = (frame->data[4] << 8) | frame->data[5];
			data->inv[1].out_freq = frame->data[6];
			break;

		case 0x0c4cff02:
			data->inv[0].fault = frame->data[1];
			break;

		case 0x0c4cff03:
			data->inv[1].fault = frame->data[1];
			break;
	}
}

/***************模组数据解析***************/

//模组故障代码转换(位变量转换为数字变量)
static uint8_t mv_fault_convert(uint8_t *buff)
{
	uint8_t ret = 0;
	uint32_t i, j;

	for (i = 0; i < 8; i++)
	{
		for (j = 0; j < 8; j++)
		{
			if (buff[i] >> j)//对应位值为1表示有故障
				ret = 8 * i + j + 1;
		}
	}
	return ret;
}

static void get_mv_data(struct can_frame *frame, udp_data_req_t *data)
{
	switch (frame->can_id & CAN_EFF_MASK)
	{
		case 0x1830ff06:
			data->mv.fault_level = (frame->data[1] >> 3) & 0x7;
			data->mv.voltage = (frame->data[2] << 8) | frame->data[3];
			data->mv.current = (frame->data[4] << 8) | frame->data[5];
			data->mv.SOC = frame->data[7] * 4 / 10;
			break;

		case 0x1831ff06:
			data->mv.batt_volt = ((frame->data[4] << 8) | frame->data[5]) / 100;
			break;

		case 0x1832ff06:
			data->mv.max_volt = ((frame->data[0] << 8) | frame->data[1]) / 10;
			data->mv.min_volt = ((frame->data[2] << 8) | frame->data[3]) / 10;
			data->mv.max_temp = frame->data[6];
			data->mv.min_temp = frame->data[7];
			break;

		case 0x1833ff06:
			data->mv.IR_value = (frame->data[2] << 8) | frame->data[3];
			break;

		case 0x1835ff06:
			data->mv.chg_stat = (frame->data[0] >> 2) & 0x7;
			break;

		case 0x1836ff06:
			data->mv.fault = mv_fault_convert(frame->data);
			break;
	}
}

/***************DCDC数据解析***************/

static void get_dcdc_data(struct can_frame *frame, udp_data_req_t *data)
{
	data->dcdc.in_volt = (frame->data[1] << 8) | frame->data[0];
	data->dcdc.out_volt = frame->data[2] * 5;
	data->dcdc.out_curr = frame->data[3];
	data->dcdc.temp = frame->data[5];
	data->dcdc.work_stat = frame->data[6] & 0x3;
	data->dcdc.fault = (frame->data[6] >> 2) & 0xf;
}

/***************服务器下电***************/

static void server_power_off(void)
{
    write_file("power-off\n");
	printf("power off!\n");
	system("sudo shutdown -h now");
}

/***************整车其它数据解析***************/

//机车故障代码转换(位变量转换为数字变量)
static uint8_t loco_fault_convert(uint8_t *buff)
{
	uint8_t ret = 0;
	uint32_t i, j;

	for (i = 1; i < 8; i++)
	{
		for (j = 0; j < 8; j++)
		{
			if (buff[i] >> j)//对应位值为1表示有故障
				ret = 8 * (i - 1) + j + 1;
		}
	}
	return ret;
}

static void get_loco_data(struct can_frame *frame, udp_data_req_t *data)
{
	switch (frame->can_id & CAN_EFF_MASK)
	{
		case 0x1861ff01:
			data->loco.speed = frame->data[4];
			data->loco.mode = (frame->data[7] >> 2) & 0x3;
			data->loco.motor = (frame->data[7] >> 4) & 0x3;
			data->loco.run_dir = (frame->data[7] >> 6) & 0x3;
			if ((frame->data[6] & 0xf0) == 0xc0)//服务器下电
				server_power_off();
			break;

		case 0x1862ff01:
			data->loco.scram = (frame->data[0] >> 7) & 0x1;
			data->loco.gear_dir = (frame->data[0] >> 5) & 0x3;
			data->loco.gear = frame->data[0] & 0x1f;
			if (data->loco.gear_dir == FWD_RUN || data->loco.gear_dir == BCK_RUN)
				data->loco.gear++;
			data->loco.point = (frame->data[1] >> 6) & 0x3;
			break;

		case 0x1863ff01:
			data->loco.fault_level = (frame->data[0] >> 5) & 0x7;
			data->loco.fault = loco_fault_convert(frame->data);
			break;

		case 0x1864ff01:
			data->sample.torque[0] = (frame->data[0] << 8) | frame->data[1];
			data->sample.torque[1] = (frame->data[2] << 8) | frame->data[3];
			data->sample.torque[2] = (frame->data[4] << 8) | frame->data[5];
			data->sample.torque[3] = (frame->data[6] << 8) | frame->data[7];
			break;

		case 0x1865ff01:
			data->sample.pressure[0] = ((frame->data[0] << 8) | frame->data[1]) / 10;
			data->sample.pressure[1] = ((frame->data[2] << 8) | frame->data[3]) / 10;
			data->sample.pressure[2] = ((frame->data[4] << 8) | frame->data[5]) / 10;
			data->sample.pressure[3] = ((frame->data[6] << 8) | frame->data[7]) / 10;
			break;
	}
}

/***************CAN转UDP线程***************/

void *mtu_ask_ccu(void *arg)
{
	int len;
	struct sockaddr_in dest_addr;
	struct can_frame frame;
	udp_cmd_req_t udp_cmd_req;

	printf("Thread mtu_ask_ccu is OK!\n");

	dest_addr.sin_family = AF_INET;//IPv4
	dest_addr.sin_port = htons(3270);//服务器端口号
	dest_addr.sin_addr.s_addr = inet_addr("192.168.11.130");//服务器IP

	memset(&udp_cmd_req, 0, sizeof(udp_cmd_req));
	udp_cmd_req.type = MTU_TO_CCU;
	udp_cmd_req.cmd = FRAME_CTRL;

	can_filter_set();
	sendto(sock_mtu_eth, &udp_cmd_req, sizeof(udp_cmd_req), 0, 
			(struct sockaddr *)&dest_addr, sizeof(dest_addr));//转发到CCU
	while (1)
	{
		len = read(sock_mtu_can, &frame, sizeof(frame));
		if (len > 0)
		{
			switch (frame.can_id & CAN_EFF_MASK)
			{
				case 0x18800701://机车向CCU询问控制命令
					memcpy(&udp_cmd_req.seq, &frame.data[0], 4);
					sendto(sock_mtu_eth, &udp_cmd_req, sizeof(udp_cmd_req), 0,
						(struct sockaddr *)&dest_addr, sizeof(dest_addr));//转发到CCU
					break;

				case 0x0c44ff02:
				case 0x0c44ff03:
				case 0x0c46ff02:
				case 0x0c46ff03:
				case 0x0c48ff02:
				case 0x0c48ff03:
				case 0x0c4cff02:
				case 0x0c4cff03:
					get_inv_data(&frame, &udp_data_req);//获取变频器数据
					break;

				case 0x1830ff06:
				case 0x1831ff06:
				case 0x1832ff06:
				case 0x1833ff06:
				case 0x1835ff06:
				case 0x1836ff06:
					get_mv_data(&frame, &udp_data_req);//获取模组数据
					break;

				case 0x18ff1519:
					get_dcdc_data(&frame, &udp_data_req);//获取DCDC数据
					break;

				case 0x1861ff01:
				case 0x1862ff01:
				case 0x1863ff01:
				case 0x1864ff01:
				case 0x1865ff01:
					get_loco_data(&frame, &udp_data_req);//获取整车其它数据
					break;
			}
		}
	}
}

/***************UDP转CAN线程***************/

//字节控制命令转换为位控制命令
static uint8_t cmd_swap(udp_cmd_res_t *cmd)
{
	uint8_t ret = 0;

	if (cmd->f_light)
		ret |= 0x1;
	else
		ret &= ~0x1;
	if (cmd->b_light)
		ret |= 0x2;
	else
		ret &= ~0x2;
	if (cmd->alarm)
		ret |= 0x4;
	else
		ret &= ~0x4;
	if (cmd->siren)
		ret |= 0x8;
	else
		ret &= ~0x8;
	if (cmd->reset)
		ret |= 0x10;
	else
		ret &= ~0x10;
	if (cmd->scram)
		ret |= 0x20;
	else
		ret &= ~0x20;
	return ret;
}


/* 远程控制记录日记 */
void logging(udp_cmd_res_t *cmd)
{
	
	static char file_name[40];
	static char floder[13] = "../ctrl_cmd";
	static char now_day[3], new_day[3]; 
	static FILE* ctrl_data_log = NULL;
	time_t new_time = time(NULL);
	struct tm *bktime = localtime(&new_time);
	
	strftime(new_day, sizeof(new_day), "%d", bktime);
	char sttr[] = "ctrl    gear   f_light b_light alarm   siren   reset   scram       time\n";
	sprintf(file_name, "%s/ctrl_cmd_of_%sday.log", floder, new_day);
	if (ctrl_data_log == NULL)
	{
		if (access(floder, F_OK))
			mkdir(floder, 0755);
		ctrl_data_log = fopen(file_name, "a+");
		if (ctrl_data_log)
			fwrite(sttr, 1, strlen(sttr), ctrl_data_log);
		else
			return;
		strcpy(now_day, new_day);
	}
	else if (strcmp(now_day, new_day))
	{
		fclose(ctrl_data_log);
		ctrl_data_log = NULL;
		ctrl_data_log = fopen(file_name, "w+");
		if (ctrl_data_log)
			fwrite(sttr, 1, strlen(sttr), ctrl_data_log);
		else
			return;
		strcpy(now_day, new_day);
	}
	char write_buf[50] = {0};
	char sttime[20] = {0};
	strftime(sttime, sizeof(sttime), "%Y-%m-%d %H:%M:%S", bktime);
    sprintf(write_buf, "%2d\t%2d\t%2d\t%2d\t%2d\t%2d\t%2d\t%2d\t%s\n", cmd->ctrl, cmd->gear, cmd->f_light, cmd->b_light, cmd->alarm, cmd->siren, cmd->reset, cmd->scram, sttime);
	fwrite(write_buf, 1, strlen(write_buf), ctrl_data_log);
	fflush(ctrl_data_log);
}


void *ccu_res_mtu(void *arg)
{
	int len;
	socklen_t sin_len;
	struct sockaddr_in get_addr;
	struct can_frame frame;
	udp_cmd_res_t *udp_cmd_res;
	udp_data_res_t *udp_data_res;
	can_cmd_res_t *can_cmd_res;
	uint8_t udp_data[32];

	printf("Thread ccu_res_mtu is OK!\n");

	memset(&frame, 0, sizeof(frame));
	frame.can_id = 0x18810107 | CAN_EFF_FLAG;//扩展帧
	frame.can_dlc = 8;//数据长度

	udp_cmd_res = (udp_cmd_res_t *)udp_data;
	udp_data_res = (udp_data_res_t *)udp_data;
	can_cmd_res = (can_cmd_res_t *)frame.data;

	while (1)
	{
		len = recvfrom(sock_mtu_eth, udp_data, 32, 0, (struct sockaddr *)&get_addr, &sin_len);
		if (len > 0)
		{
			if (len == 24 && udp_data[0] == CCU_TO_MTU && udp_data[1] == FRAME_CTRL)//收到命令应答
			{
				printf("Get ccu cmd response!\n");
				// 每接受5次数据记录1次
				static int get_count = 5;
				get_count--;
				if (!get_count)
				{
					logging(udp_cmd_res);
					get_count = 5;
				}
				memcpy(&can_cmd_res->seq, &udp_cmd_res->seq, 6);//生成控制命令
				can_cmd_res->dev_ctrl = cmd_swap(udp_cmd_res);

				if ((udp_cmd_res->status & 0x1) || (udp_cmd_res->status & 0x2))//MTU/RCU与CCU连接断开
					memset(&can_cmd_res->ctrl, 0, 3);//清0控制命令
				rmt_gear = can_cmd_res->gear;//记录远程档位

				write(sock_mtu_can, &frame, sizeof(frame));//转发给机车
			}
			
			if (len == 18 && udp_data[0] == CCU_TO_MTU && udp_data[1] == FRAME_DATA)//收到数据应答
			{
				printf("Get ccu data response!\n");
				
				memcpy(&locate, &udp_data_res->locate.ache, sizeof(locate));//拷贝定位信息
			}
		}
		else
		{
			memset(&locate, 0, sizeof(locate));
			printf("No location info!\n");
		}
	}
}

/***************整车数据上传线程***************/

//当前运行档位获取
static void get_gear(uint8_t rmt_gear, loco_t *loco)
{
	if (loco->mode != LOCAL_CTRL)//非本地控制时将远程档位分解回传
	{
		loco->gear = 0;//清0档位
		loco->gear_dir = STOP_RUN;
		loco->point = STOP_RUN;

		if (rmt_gear == 0x10)//前点
			loco->point = FWD_RUN;
		else if (rmt_gear == 0x20)//后点
			loco->point = BCK_RUN;
		else if (rmt_gear & 0x10)//前进档
		{
			loco->gear_dir = FWD_RUN;
			loco->gear = rmt_gear & 0xf;
		}
		else if (rmt_gear & 0x20)//后退档
		{
			loco->gear_dir = BCK_RUN;
			loco->gear = rmt_gear & 0xf;
		}
	}
}

void *mtu_data_up(void *arg)
{
	uint16_t distance;
	int i;
	struct sockaddr_in dest_addr;
	struct can_frame frame;
	
	printf("Thread mtu_data_up is OK!\n");
	
	dest_addr.sin_family = AF_INET;//IPv4
	dest_addr.sin_port = htons(3270);//服务器端口号
	dest_addr.sin_addr.s_addr = inet_addr("192.168.0.205");//服务器IP

	frame.can_id = 0x18820107 | CAN_EFF_FLAG;//扩展帧
	frame.can_dlc = 8;//数据长度
	memset(frame.data, 0xff, 8);
	
	memset(&udp_data_req, 0, sizeof(udp_data_req));
	udp_data_req.type = MTU_TO_CCU;
	udp_data_req.cmd = FRAME_DATA;
	udp_data_req.mtu_id = MTU_ID;

	while (1)
	{
		get_gear(rmt_gear, &udp_data_req.loco);

		sendto(sock_mtu_eth, &udp_data_req, sizeof(udp_data_req), 0,
			(struct sockaddr *)&dest_addr, sizeof(dest_addr));//发送整车数据给CCU
		udp_data_req.seq++;

		if (locate.distance > 5000)//最大范围500m
			distance = 500;
		else
			distance = locate.distance / 10;
		if (locate.distance == 0)//0距离表示定位无效
			distance = 0x3ff;
		frame.data[2] = ((distance >> 6) & 0xf) | 0xf0;//将盾构定位数据写入发送帧
		frame.data[3] = (distance << 2) & 0xfc;
		write(sock_mtu_can, &frame, sizeof(frame));//转发给机车

		usleep(200000);
	}
}

